/** @file tex_dds.cpp
    @brief DDS images */

#include "tex/tex_dds.h"

using namespace G3;

boost::shared_ptr<G3::BITMAP> G3::LoadDDS (STREAM *Stream, LOG *Log) {
	dword Format = 0;
	dword LinearSize = 0;
	dword MipMapCount = 0;
	dword Width = 0;
	dword Height = 0;
	char filecode [4];
	int factor = 0;

	Assert (Stream, "LoadDDS: Stream argument is a NULL pointer");

	if (!Stream->Read ((byte *) filecode, 4)) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Failed to read DDS signature (4 bytes) from the stream..\n");
		return boost::shared_ptr<BITMAP>();
	}

	if (strncmp (filecode, "DDS ", 4) != 0) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Failed to verify DDS signature..\n");
		return boost::shared_ptr<BITMAP>();
	}
	// Read the DDS format (DXT1, DXT3, DXT5)
	Stream->Seek (G3_DDS_FOURCC_OFFSET, STREAM::STREAM_SET);
	if (!Stream->Read (((byte *) &Format), sizeof (dword))) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Failed to read DDS format (%d bytes) from the stream..\n", sizeof (dword));
		return boost::shared_ptr<BITMAP>();
	}
	// Read the linear size of the DDS
	Stream->Seek (G3_DDS_LINEAR_SIZE_OFFSET, STREAM::STREAM_SET);
	if (!Stream->Read (((byte *) &LinearSize), sizeof (dword))) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Failed to read DDS linear size (%d bytes) from the stream..\n", sizeof (dword));
		return boost::shared_ptr<BITMAP>();
	}
	// Read the mipmap count of the DDS
	Stream->Seek (G3_DDS_MIPMAP_COUNT_OFFSET, STREAM::STREAM_SET);
	if (!Stream->Read (((byte *) &MipMapCount), sizeof (dword))) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Failed to read DDS mipmap count (%d bytes) from the stream..\n", sizeof (dword));
		return boost::shared_ptr<BITMAP>();
	}
	// Read the width of the DDS largest mipmap
	Stream->Seek (G3_DDS_WIDTH_OFFSET, STREAM::STREAM_SET);
	if (!Stream->Read (((byte *) &Width), sizeof (dword))) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Failed to read DDS width (%d bytes) from the stream..\n", sizeof (dword));
		return boost::shared_ptr<BITMAP>();
	}
	// Read the height of the DDS largest mipmap
	Stream->Seek (G3_DDS_HEIGHT_OFFSET, STREAM::STREAM_SET);
	if (!Stream->Read (((byte *) &Height), sizeof (dword))) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Failed to read DDS height (%d bytes) from the stream..\n", sizeof (dword));
		return boost::shared_ptr<BITMAP>();
	}

	boost::shared_ptr<BITMAP> Bitmap (new BITMAP);

	if (!Bitmap) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Not enough memory for a BITMAP class (at least %ld more bytes required)..\n", sizeof (BITMAP));
		return boost::shared_ptr<BITMAP>();
	}

	switch (Format) {
		case FOURCC_DXT1:
			// DXT1's compression ratio is 8:1
			Bitmap->Flags |= BITMAP_COMPRESSED;
			Bitmap->Compression = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
			factor = 2;
			break;

		case FOURCC_DXT3:
			// DXT3's compression ratio is 4:1
			Bitmap->Flags |= BITMAP_COMPRESSED;
			Bitmap->Compression = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
			factor = 4;
			break;

		case FOURCC_DXT5:
			// DXT5's compression ratio is 4:1
			Bitmap->Flags |= BITMAP_COMPRESSED;
			Bitmap->Compression = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
			factor = 4;
			break;

		default:
			if (Log)
				Log->Report ("WARNING! LoadDDS: The DDS seems to be uncompressed. Skipping..\n");
			return boost::shared_ptr<BITMAP>();
	}

	if (!LinearSize) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: The DDS file is 0 in linear size\n");
		return boost::shared_ptr<BITMAP>();
	}

	if (MipMapCount > 1)
		Bitmap->Size = LinearSize * factor;
	else
		Bitmap->Size = LinearSize;

	Bitmap->Data = new byte [Bitmap->Size];

	if (!Bitmap->Data) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Not enough memory for bitmap data (at least %ld more bytes required)..\n", Bitmap->Size);
		return boost::shared_ptr<BITMAP>();
	}

	// Read DDS pixel data
	Stream->Seek (G3_DDS_PIXEL_DATA_OFFSET, STREAM::STREAM_SET);
	if (!Stream->Read (Bitmap->Data, Bitmap->Size)) {
		if (Log)
			Log->Report ("ERROR! LoadDDS: Failed to read DDS bitmap data from the stream..\n");
		delete [] Bitmap->Data;
		return boost::shared_ptr<BITMAP>();
	}

	Bitmap->Width = Width;
	Bitmap->Height = Height;
	Bitmap->NumMipMaps = MipMapCount;

	if (Format == FOURCC_DXT1)
		Bitmap->BitsPerPixel = 24;
	else
		Bitmap->BitsPerPixel = 32;

	// Mark the end of the file
	Stream->Flush ();
	//Stream->SetFileStart ();

	return Bitmap;
}
